package web;

import entity.ClientResponse;
import exception.IClientResponse;
import exception.ParamCheckFailureException;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import validator.INumberValidate;
import validator.NumValidator;
import validator.NumberValidatorIfNull;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Controller的抽象实现， 对一些通用逻辑进行了实现。其他业务Controller可继承此类，直接使用通用方法
 *
 * @author shishaolong
 * @datatime 2020/7/3 14:06
 */
public abstract class AbstractController {

    /**
     * 手机号码格式限制
     */
    public static final String MOBILE_PHONE_NUMBER_PATTERN = "^0{0,1}(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$";
    /**
     * 邮箱格式限制
     */
    public static final String EMAIL_PATTERN = "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?";


    // -------------  定义错误信息  ---------------

    public static final String DATE_STYLE_ERROR = "日期格式错误";
    public static final String NOT_NUMBER_TYPE = "非数值类型";
    public static final String NUMBER_MUST_GREAT_ZERO = "数值必须大于0";
    public static final String NUMBER_NOT_POSITIVE = "数值不能为负数";
    public static final String STATUS_TYPE_ERROR = "状态类型错误";
    public static final String PHONE_STYLE_ERROR = "手机号格式错误";
    public static final String EMAIL_STYLE_ERROR = "邮箱格式错误格式错误";

    public static final String FULL_DATE_STYLE = "yyyy-MM-dd HH:mm:ss";
    public static final String SHORT_DATE_STYLE = "yyyy-MM-dd";

    /*
    @Autowired
    private ISysDictTypeService iSysDictTypeService;
    */
    // -------------  通用的参数校验 start ---------------
    // ++ 字符串

    /**
     * 校验字符串不为空方法
     *
     * @throws ParamCheckFailureException
     */
    public static void checkNotNull(Object obj, String errorMessage) throws ParamCheckFailureException {
        // 判断是否为文件类型
        if (obj instanceof MultipartFile) {
            MultipartFile multipartFile = (MultipartFile) obj;
            if (multipartFile.isEmpty()) {
                throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
            }
        }
        if (StringUtils.isEmpty(obj)) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    /**
     * 校验手机号格式方法
     *
     * @throws ParamCheckFailureException
     */
    public static void checkPhoneStyle(String str, String errorMessage) throws ParamCheckFailureException {
        if (!genericMatcher(MOBILE_PHONE_NUMBER_PATTERN, str)) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    /**
     * 校验手机号格式方法
     *
     * @throws ParamCheckFailureException
     */
    public static void checkPhoneStyle(String str) throws ParamCheckFailureException {
        checkPhoneStyle(str, PHONE_STYLE_ERROR);
    }

    /**
     * 校验email格式方法
     *
     * @throws ParamCheckFailureException
     */
    public static void checkEmailStyle(String str, String errorMessage) throws ParamCheckFailureException {
        if (!genericMatcher(EMAIL_PATTERN, str)) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    /**
     * 校验email格式方法
     *
     * @throws ParamCheckFailureException
     */
    public static void checkEmailStyle(String str) throws ParamCheckFailureException {
        checkEmailStyle(str, EMAIL_STYLE_ERROR);
    }

    // ++ 日期类

    /**
     * 校验日期格式 并转成Date类型 校验不通过时提示默认信息（日期格式错误）
     *
     * @param dateStr          日期字符串
     * @param dateFormatterStr 日期格式字符串 形式：yyyy-MM-dd
     * @return 转换返回日期类型
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkDate2Date(String dateStr, String dateFormatterStr) throws ParamCheckFailureException {
        return checkDate2Date(dateStr, dateFormatterStr, DATE_STYLE_ERROR);
    }

    /**
     * 校验日期格式 并转成Date类型
     *
     * @param dateStr          日期字符串
     * @param dateFormatterStr 日期格式字符串 形式：yyyy-MM-dd
     * @param errorMessage     校验不通过时提示信息
     * @return 转换返回日期类型
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkDate2Date(String dateStr, String dateFormatterStr, String errorMessage) throws ParamCheckFailureException {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormatterStr);
            sdf.setLenient(false);
            Date date = sdf.parse(dateStr);
            return date;
        } catch (Exception e) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    // - 短日期 yyyy-MM-dd 形式

    /**
     * 校验[yyyy-MM-dd]形式日期格式 并转成Date类型 校验不通过时提示默认信息（日期格式错误）
     *
     * @param dateStr 日期字符串
     * @return 转换返回日期类型
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkShortDate2Date(String dateStr) throws ParamCheckFailureException {
        return checkShortDate2Date(dateStr, DATE_STYLE_ERROR);
    }

    /**
     * 校验[yyyy-MM-dd]形式日期格式 并转成Date类型 校验不通过时提示自定义错误信息
     *
     * @param dateStr      日期字符串
     * @param errorMessage 校验不通过时提示信息
     * @return 转换返回日期类型
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkShortDate2Date(String dateStr, String errorMessage) throws ParamCheckFailureException {
        try {
            return checkDate2Date(dateStr, SHORT_DATE_STYLE);
        } catch (Exception e) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }


    // - 全日期时间 yyyy-MM-dd hh:mm:dd 形式

    /**
     * 校验[yyyy-MM-dd hh:mm:ss]形式日期格式 并转成Date类型 校验不通过时提示默认信息（日期格式错误）
     *
     * @param dateStr 日期字符串
     * @return Date
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkFullDate2Date(String dateStr) throws ParamCheckFailureException {
        return checkFullDate2Date(dateStr, DATE_STYLE_ERROR);
    }

    /**
     * 校验[yyyy-MM-dd hh:mm:ss]形式日期格式 并转成Date类型 校验不通过时提示自定义错误信息
     *
     * @param dateStr      日期字符串
     * @param errorMessage 校验不通过时提示信息
     * @return Date
     * @throws ParamCheckFailureException 参数校验失败异常
     */
    public static Date checkFullDate2Date(String dateStr, String errorMessage) throws ParamCheckFailureException {
        try {
            return checkDate2Date(dateStr, FULL_DATE_STYLE);
        } catch (Exception e) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    // ++ 小数类
    // 只判断格式

    /**
     * 校验小数格式 并转成BigDecimal形式 校验不通过提示自定义的提示信息
     *
     * @param bigStr       小数字符串
     * @param errorMassage 错误提示信息
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalType(String bigStr, String errorMassage) throws ParamCheckFailureException {
        try {
            BigDecimal bigDecimal = bigStr == null ? null : new BigDecimal(String.valueOf(bigStr));
            if (bigDecimal == null) {
                throw new ParamCheckFailureException(ClientResponse.createError(errorMassage));
            }
            return bigDecimal;
        } catch (Exception e) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMassage));
        }
    }

    /**
     * 校验小数格式 并转成BigDecimal形式 校验不通过提示默认信息非数值类型
     *
     * @param bigStr 小数字符串
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalType(String bigStr) throws ParamCheckFailureException {
        return checkBigDecimalType(bigStr, NOT_NUMBER_TYPE);
    }

    /**
     * 校验小数大于0 并转成BigDecimal形式 校验不通过提示自定义的提示信息
     *
     * @param bigStr       小数字符串
     * @param errorMassage 错误提示信息
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalGeZero(String bigStr, String errorMassage) throws ParamCheckFailureException {
        BigDecimal bigDecimal = checkBigDecimalType(bigStr, errorMassage);
        // 小于等于0报错
        if (bigDecimal.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMassage));
        }
        return bigDecimal;
    }

    /**
     * 校验小数大于0 并转成BigDecimal形式 校验不通过提示数值必须大于0
     *
     * @param bigStr 小数字符串
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalGeZero(String bigStr) throws ParamCheckFailureException {
        return checkBigDecimalGeZero(bigStr, NUMBER_MUST_GREAT_ZERO);
    }

    /**
     * 校验小数不为负数 并转成BigDecimal形式 校验不通过提示自定义的提示信息
     *
     * @param bigStr       小数字符串
     * @param errorMassage 错误提示信息
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalIncZero(String bigStr, String errorMassage) throws ParamCheckFailureException {
        BigDecimal bigDecimal = checkBigDecimalType(bigStr, errorMassage);
        // 小于等于0报错
        if (bigDecimal.compareTo(BigDecimal.ZERO) < 0) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMassage));
        }
        return bigDecimal;
    }

    /**
     * 校验小数不为负数 并转成BigDecimal形式 校验不通过提示默认信息数值不能为负数
     *
     * @param bigStr 小数字符串
     * @return
     * @throws ParamCheckFailureException
     */
    public static BigDecimal checkBigDecimalIncZero(String bigStr) throws ParamCheckFailureException {
        return checkBigDecimalIncZero(bigStr, NUMBER_NOT_POSITIVE);
    }

    // ++ 状态类校验

    /**
     * 校验所传字符是否为枚举中一个值， 并且将其转为枚举实例
     *
     * @param enumType     枚举类型
     * @param name         枚举值名
     * @param errorMessage 校验不通过的提示信息
     * @param <T>          枚举的Class文件
     * @return
     * @throws ParamCheckFailureException
     */
    public static <T extends Enum<T>> T checkStatusEnum(Class<T> enumType, String name, String errorMessage) throws ParamCheckFailureException {
        try {
            return Enum.valueOf(enumType, name);
        } catch (Exception e) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    /**
     * 校验所传字符是否为枚举中一个值， 并且将其转为枚举实例。校验不通过 时显示默认提示：状态类型错误
     *
     * @param enumType 枚举类型
     * @param name     枚举值名
     * @param <T>      枚举的Class文件
     * @return
     * @throws ParamCheckFailureException
     */
    public static <T extends Enum<T>> T checkStatusEnum(Class<T> enumType, String name) throws ParamCheckFailureException {
        return checkStatusEnum(enumType, name, STATUS_TYPE_ERROR);
    }

    /**
     * 校验所传字符是否在定义的List中
     *
     * @param statusList   枚举类型
     * @param name         枚举值名
     * @param errorMessage 校验不通过的提示信息
     * @return
     * @throws ParamCheckFailureException
     */
    public static void checkStatusList(List<String> statusList, String name, String errorMessage) throws ParamCheckFailureException {
        if (statusList == null || statusList.isEmpty()) {
            return;
        }
        if (!statusList.contains(name)) {
            throw new ParamCheckFailureException(err(errorMessage));
        }
    }

    /**
     * 校验所传字符是否在定义的Array中
     *
     * @param statusArray  枚举类型
     * @param name         枚举值名
     * @param errorMessage 校验不通过的提示信息
     * @return
     * @throws ParamCheckFailureException
     */
    public static void checkStatusArray(String[] statusArray, String name, String errorMessage) throws ParamCheckFailureException {
        checkStatusList(Arrays.asList(statusArray), name, errorMessage);
    }

    // ++ 通用的正则校验

    /**
     * 正则校验
     *
     * @param regStr       正则表达式
     * @param checkStr     需要校验的字符串
     * @param errorMessage 错误提示信息
     * @throws ParamCheckFailureException
     */
    public static void checkReg(String regStr, String checkStr, String errorMessage) throws ParamCheckFailureException {
        Pattern pattern = Pattern.compile(regStr);
        checkReg(pattern, checkStr, errorMessage);
    }

    /**
     * 正则校验
     *
     * @param pattern      正则表达式
     * @param checkStr     需要校验的字符串
     * @param errorMessage 错误提示信息
     * @throws ParamCheckFailureException
     */
    public static void checkReg(Pattern pattern, String checkStr, String errorMessage) throws ParamCheckFailureException {
        if (!genericMatcher(pattern, checkStr)) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }

    // ++ 校验字典值

    /**
     * 校验字典值
     *
     * @param hrmCommonDataTypeEnum
     * @param enterpriseNature
     * @param errorMessage
     * @throws ParamCheckFailureException
     */
    /*public void checkDictValue(HrmCommonDataTypeEnum hrmCommonDataTypeEnum, String enterpriseNature, String errorMessage) throws ParamCheckFailureException {
        List<SysDictData> sysDictData = iSysDictTypeService.selectDictDataByType(hrmCommonDataTypeEnum.name());
        // 只要有一个匹配上就为true
        boolean flag = sysDictData.stream().anyMatch(sysDictItem -> enterpriseNature.equals(sysDictItem.getDictValue()));
        // 没有一个匹配上抛出异常
        if (!flag) {
            throw new ParamCheckFailureException(ClientResponse.createError(errorMessage));
        }
    }*/
    // -------------  通用的参数校验 end ---------------


    // -------------  通用的工具类方法 start -------------

    /**
     * 通用的正则校验方法
     *
     * @param regexExpre
     * @param checkStr
     * @return
     */
    public static boolean genericMatcher(String regexExpre, String checkStr) {
        Pattern pattern = Pattern.compile(regexExpre);
        return genericMatcher(pattern, checkStr);
    }

    /**
     * 通用的正则校验方法
     *
     * @param pattern
     * @param checkStr
     * @return
     */
    public static boolean genericMatcher(Pattern pattern, String checkStr) {
        Matcher matcher = pattern.matcher(checkStr);
        return matcher.matches();
    }

    /**
     * 将传入的字符按传入的分隔符拆分后转成List
     *
     * @param str       用分隔符隔开的字符串
     * @param separator 分隔符
     * @return
     */
    public static List<String> strSplitToList(String str, String separator) {
        Objects.requireNonNull(str, "str不能为null");
        Objects.requireNonNull(separator, "separator不能为null");
        List<String> list = Stream.of(str.split(separator)).collect(Collectors.toList());
        return list;
    }

    /**
     * 将传入的字符按逗号分隔符拆分后转成List, str为null或空时会返回空的List
     *
     * @param str 用分隔符隔开的字符串
     * @return
     */
    public static List<String> strSplitToList(String str) {
        return StringUtils.isEmpty(str) ? new ArrayList<>() : strSplitToList(str, ",");
    }

    /**
     * 将字符串List转成用指定字符分隔符隔开的字符串
     *
     * @param stringList 字符串List
     * @param separator  分隔符
     * @return
     */
    public static String listToStr(List<String> stringList, String separator) {
        Objects.requireNonNull(separator, "separator 不能为null");
        return Optional.ofNullable(stringList).orElse(new ArrayList<>()).stream().collect(Collectors.joining(separator));
    }

    /**
     * 将字符串List转成用逗号隔开的字符串
     *
     * @param stringList
     * @return
     */
    public static String listToStr(List<String> stringList) {
        return listToStr(stringList, ",");
    }

    /**
     * 将request中的param转成字符串
     *
     * @param map
     * @return
     */
    public static String getParamString(Map<String, String[]> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String[]> e : map.entrySet()) {
            sb.append(e.getKey()).append("=");
            String[] value = e.getValue();
            if (value != null && value.length == 1) {
                sb.append(value[0]).append("&");
            } else {
                sb.append(Arrays.toString(value)).append("&");
            }
        }
        return sb.toString();
    }
    // -------------  通用的工具类方法 end ---------------

    // -------------  响应的封装方法 start ---------------

    /**
     * 成功响应
     *
     * @return
     */
    public static ClientResponse ok() {
        return ok("获取成功");
    }

    /**
     * 成功响应
     *
     * @param msg
     * @return
     */
    public static ClientResponse ok(String msg) {
        return ClientResponse.createOk(msg);
    }

    /**
     * 错误响应
     *
     * @param msg
     * @return
     */
    public static ClientResponse err(String msg) {
        return ClientResponse.createError(msg);
    }

    /**
     * 错误响应
     *
     * @param exception 异常
     * @return
     */
    public static ClientResponse err(Exception exception) {
        if (exception instanceof IClientResponse) {
            IClientResponse iClientResponse = (IClientResponse) exception;
            return err(iClientResponse);
        }
        exception.printStackTrace();
        return err();
    }

    /**
     * 错误响应
     *
     * @param iClientResponse 实现了IClientResponse接口的类
     * @return
     */
    private static ClientResponse err(IClientResponse iClientResponse) {
        return iClientResponse.getClientResponse();
    }


    /**
     * 错误响应
     *
     * @return
     */
    public static ClientResponse err() {
        return ClientResponse.createError("系统错误");
    }

    // ++ 后台系统相关


    // -------------  响应的封装方法 end ---------------
    public INumberValidate ckNum(String paramValue, String paramName) throws ParamCheckFailureException {
        return new NumValidator(paramValue, paramName);
    }

    public INumberValidate ckNumIfNotNull(String paramValue, String paramName) throws ParamCheckFailureException {
        if (utils.StringUtils.isEmpty(paramValue)) {
            return new NumberValidatorIfNull();
        } else {
            return ckNum(paramValue, paramName);
        }
    }
}
